package com.bwton.msx.ccb.ui;

import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.text.TextUtils;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.bwton.msx.ccb.Constants;
import com.bwton.msx.ccb.R;
import com.bwton.msx.ccb.utils.DensityUtil;
import com.bwton.msx.ccb.utils.FileUtil;
import com.bwton.msx.ccb.utils.MotionUtil;
import com.bwton.msx.ccb.utils.PreferenceUtil;
import com.bwton.msx.ccb.utils.SourceManager;
import com.bwton.msx.ccb.utils.Toaster;
import com.bwton.msx.ccb.widget.WaterRippleView;
import com.sensetime.ssidmobile.sdk.liveness.OnOnlineDataSourceListener;
import com.sensetime.ssidmobile.sdk.liveness.OnlineCallback;
import com.sensetime.ssidmobile.sdk.liveness.STLiveness;
import com.sensetime.ssidmobile.sdk.liveness.constants.STDetectorMode;
import com.sensetime.ssidmobile.sdk.liveness.constants.STResultCode;
import com.sensetime.ssidmobile.sdk.liveness.constants.STLivenessStage;
import com.sensetime.ssidmobile.sdk.liveness.constants.STPixelFormat;
import com.sensetime.ssidmobile.sdk.liveness.model.STResult;
import com.sensetime.ssidmobile.sdk.liveness.model.STResultImage;
import com.sensetime.ssidmobile.sdk.liveness.model.STSignedData;
import com.sensetime.ssidmobile.sdk.liveness.model.config.DefakeConfig;
import com.sensetime.ssidmobile.sdk.liveness.model.config.DetectorConfig;
import com.sensetime.ssidmobile.sdk.liveness.model.config.ModelsConfig;
import com.sensetime.ssidmobile.sdk.liveness.model.config.MotionConfig;

import java.util.ArrayList;
import java.util.List;

import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

public class InteractiveActivity extends LivenessActivity {

    public static void start(Context context) {
        context.startActivity(new Intent(context, InteractiveActivity.class));
    }
    public static void startForResult(Context context) {
        context.startActivity(new Intent(context, InteractiveActivity.class));
    }
    private STLiveness mSTLiveness;
    private Rect mApertureRect;
    private boolean isInput;
    private MotionStepControlFragment mMotionStepControlFragment;
    private List<MotionStep> mMotionSteps;
    private @STLivenessStage
    int mCurrentStage = STLivenessStage.NONE;

    @Override
    protected void init(DetectorConfig detectorConfig) {
        // 创建扫描器
        mSTLiveness = new STLiveness();
        // 配置扫描器阈值
        mSTLiveness.init(getBaseContext(), detectorConfig, this);
        mMotionSteps = new ArrayList<>();
        for (int motion : PreferenceUtil.getSequence()) {
            mMotionSteps.add(new MotionStep(motion, getString(MotionUtil.getMotionNameResID(motion)), MotionStep.StepState.STEP_UNDO));
        }
        this.mMotionStepControlFragment = MotionStepControlFragment.newInstance();
        this.mMotionStepControlFragment.addMotionStepBeans(mMotionSteps);
        final FragmentManager fragmentManager = this.getSupportFragmentManager();
        final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.layout_motions, this.mMotionStepControlFragment, "MotionStep");
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        fragmentTransaction.commitAllowingStateLoss();
    }

    @Override
    protected void onDestroy() {
        if (mSTLiveness != null) {
            mSTLiveness.destroy();
        }
        super.onDestroy();
    }

    @Override
    protected DetectorConfig createDetectorConfig() {
        Context context = this;
        String destFilePath = context.getCacheDir().getAbsolutePath() + "/models/";
        return new DetectorConfig.Builder()
                .setLicenseContent(FileUtil.getLicenseContent(context, Constants.FACE_LICENSE_FILE_NAME)) //加载 license内容
                .setModelsConfig(new ModelsConfig.Builder()
                        .setHunterModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_HUNTER_FILE_NAME, destFilePath)) // 具体模型路径
                        .setAlignModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_ALIGN_FILE_NAME, destFilePath))
                        .setAugustModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_AUGUST_FILE_NAME, destFilePath))
                        .setEyeStateModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_EYESTATE_FILE_NAME, destFilePath))
                        .setHeadPoseModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_HEADPOSE_FILE_NAME, destFilePath))
                        .setLivenessModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_LIVENESS_FILE_NAME, destFilePath))
                        .setPageantModelPath(FileUtil.copyAssetsToFile(context, Constants.MODEL_PAGEANT_FILE_NAME, destFilePath))
                        .build())
                .build();
    }

    @Override
    public void onInit(STResult result) {
        if (result.code == STResultCode.OK) {
            // 初始化完成
            isInput = true;
            mSTLiveness.setQualityConfig(PreferenceUtil.getQualityConfig());
            mSTLiveness.setLivenessConfig(PreferenceUtil.qetLivenessConfig());
            mSTLiveness.setMotionConfig(new MotionConfig.Builder()
                    .setMotionTimeoutMillis(PreferenceUtil.getMotionTimeOutMillis())
                    .setReadyTimeoutMillis(PreferenceUtil.getMotionReadyTimeOutMillis())
                    .setMotions(PreferenceUtil.getSequence())
                    .build());

            DefakeConfig defakeConfig = PreferenceUtil.getDefakeConfig();
            if (defakeConfig.isDefake) {
                mSTLiveness.setDefakeConfig(defakeConfig);
                mSTLiveness.setOnlineDataSource(new OnOnlineDataSourceListener() {
                    @Override
                    public void uploadData(byte[] bytes, final OnlineCallback onlineCallback) {
                        // defake接口地址
                        String defakeUrl = "";
                        if (TextUtils.isEmpty(defakeUrl)) {
                            Toaster.show("defake服务未配置，无法使用该功能");
                        } else {
                            SourceManager.getInstance().postReaction(defakeUrl, bytes)
                                    .map(new Function<String, byte[]>() {
                                        @Override
                                        public byte[] apply(String defakeBase64) throws Exception {
                                            return Base64.decode(defakeBase64, Base64.NO_WRAP);
                                        }
                                    })
                                    .subscribeOn(Schedulers.io())
                                    .observeOn(AndroidSchedulers.mainThread())
                                    .subscribe(new Consumer<byte[]>() {
                                        @Override
                                        public void accept(byte[] bytes) throws Exception {
                                            onlineCallback.call(bytes);
                                        }
                                    }, new Consumer<Throwable>() {
                                        @Override
                                        public void accept(Throwable throwable) throws Exception {
                                            throwable.printStackTrace();
                                        }
                                    });
                        }
                    }
                });
            }

            mSTLiveness.setDetectorMode(STDetectorMode.INTERACTIVE);
            mFaceOverlayView.post(new Runnable() {
                @Override
                public void run() {
                    mApertureRect = mCameraPreview.convertViewRectToCameraPreview(mFaceOverlayView.getMaskBounds());
                    mSTLiveness.setTargetRect(mApertureRect);
                    mSTLiveness.start();

                }
            });
        } else {
            // 初始化异常
            isInput = false;
            Toaster.show("初始化异常，异常码:" + result);
            onFailure(result, null, null, null, null);
        }
    }

    @Override
    public void onUpdateStage(@STLivenessStage int stage) {
        mMotionStepControlFragment.updateMotionStep(mCurrentStage, stage);
        mCurrentStage = stage;
        showStage(mCurrentStage);
    }

    private void showStage(@STLivenessStage int stage) {
        switch (stage) {
            case STLivenessStage.BLINK:
                changeHint(R.string.common_blink_description);
                break;
            case STLivenessStage.MOUTH:
                changeHint(R.string.common_mouth_description);
                break;
            case STLivenessStage.NOD:
                changeHint(R.string.common_nod_description);
                break;
            case STLivenessStage.YAW:
                changeHint(R.string.common_yaw_description);
                break;
            case STLivenessStage.DEFAKE:
                changeHint(R.string.status_check);
                break;
            case STLivenessStage.SYSTEM_ERROR:
                Toaster.show(getString(R.string.error_system));
                break;
            default:
                break;
        }
    }

    @Override
    public void onResult(STResult result, STResultImage originalResultImage, STResultImage cropResultImage, STResultImage[] stageResultImages, STSignedData signedData) {
        if (result.code == STResultCode.OK) {
            onSuccess(originalResultImage, cropResultImage, stageResultImages, signedData);
        } else {
            onFailure(result, originalResultImage, cropResultImage, stageResultImages, signedData);
        }
    }

    public void onFailure(final STResult stResult, STResultImage originalResultImage, STResultImage cropResultImage, STResultImage[] stageResultImages, STSignedData signedData) {
        if (stResult.code == STResultCode.FACE_LOST) {
            if (isFoundFace) {
                restartDetector();
            } else {
                changeHint(R.string.error_status_out_bound);
                Toast.makeText(this, String.format("重新启动", restartTimes), Toast.LENGTH_SHORT).show();
                restartLiveness();
            }
            return;
        }
        final ResultActivity.Result result = saveResult(originalResultImage, cropResultImage, stageResultImages, signedData);
        result.resultCode = stResult.code;
        result.resultMessage = stResult.message;
        startResultActivity(result);
    }

    public void onSuccess(STResultImage originalResultImage, STResultImage cropResultImage, STResultImage[] stageResultImages, STSignedData signedData) {
        final ResultActivity.Result result = saveResult(originalResultImage, cropResultImage, stageResultImages, signedData);
        result.resultCode = STResultCode.OK;
        Intent intent = new Intent();
        intent.putStringArrayListExtra("image",new ArrayList<>(result.bitmapFilePaths));
        //修改 跳转回原来页面
        setResult(RESULT_OK,intent);
        finish();
    }

    @Override
    protected void startResultActivity(ResultActivity.Result result) {
        ResultActivity.start(InteractiveActivity.this, result);
        finish();
    }

    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {
        if (mSTLiveness != null && isInput) {
            Camera.Size previewSize = camera.getParameters().getPreviewSize();
            mSTLiveness.input(bytes, STPixelFormat.NV21, previewSize.width, previewSize.height, mFaceOrientation);
        }
    }

    @Override
    protected void showDetectingHint() {
        showStage(mCurrentStage);
    }

    @Override
    protected void restartLiveness() {
        if (mSTLiveness != null) {
            mSTLiveness.restart();
            mMotionStepControlFragment.restart();
            mCurrentStage = STLivenessStage.NONE;
        }
    }

    public static class MotionStepControlFragment extends Fragment {

        private List<MotionStep> mMotionStepBeans = new ArrayList<>();

        private View mLayoutView = null;

        public static MotionStepControlFragment newInstance() {
            return new MotionStepControlFragment();
        }

        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            if (this.mMotionStepBeans.isEmpty()) {
                return null;
            }
            this.mLayoutView = inflater.inflate(getLayoutResource(), container, false);
            initView();
            return this.mLayoutView;
        }

        private void initView() {
            switch (this.mMotionStepBeans.size()) {
                case 1:
                    initFirstStep();
                    break;
                case 2:
                    initFirstStep();
                    initSecondStep();
                    break;
                case 3:
                    initFirstStep();
                    initSecondStep();
                    initThirdStep();
                    break;
                case 4:
                    initFirstStep();
                    initSecondStep();
                    initThirdStep();
                    initForthStep();
                    break;
                default:
                    break;
            }
        }

        private void initFirstStep() {
            if (this.mLayoutView == null || this.mMotionStepBeans.size() < 1) {
                return;
            }
            this.mMotionStepBeans.get(0).bindView(
                    (TextView) this.mLayoutView.findViewById(R.id.txt_step_one),
                    this.mLayoutView.<WaterRippleView>findViewById(R.id.ripple_step_first),
                    null);
        }

        private void initSecondStep() {
            if (this.mLayoutView == null || this.mMotionStepBeans.size() < 2) {
                return;
            }
            this.mMotionStepBeans.get(1).bindView(
                    this.mLayoutView.<TextView>findViewById(R.id.txt_step_two),
                    this.mLayoutView.<WaterRippleView>findViewById(R.id.ripple_step_second),
                    this.mLayoutView.findViewById(R.id.line_first_to_second)
            );
        }

        private void initThirdStep() {
            if (this.mLayoutView == null || this.mMotionStepBeans.size() < 3) {
                return;
            }
            this.mMotionStepBeans.get(2).bindView(
                    (TextView) this.mLayoutView.findViewById(R.id.txt_step_three),
                    this.mLayoutView.<WaterRippleView>findViewById(R.id.ripple_step_third),
                    this.mLayoutView.findViewById(R.id.line_second_to_third)
            );
        }

        private void initForthStep() {
            this.mMotionStepBeans.get(3).bindView(
                    (TextView) this.mLayoutView.findViewById(R.id.txt_step_four),
                    this.mLayoutView.<WaterRippleView>findViewById(R.id.ripple_step_fourth),
                    this.mLayoutView.findViewById(R.id.line_third_to_fourth)
            );
        }

        private int getLayoutResource() {
            if (this.mMotionStepBeans.isEmpty()) {
                return -1;
            }
            switch (this.mMotionStepBeans.size()) {
                case 1:
                    return R.layout.layout_one_motion_step;
                case 2:
                    return R.layout.layout_two_motion_steps;
                case 3:
                    return R.layout.layout_three_motion_steps;
                case 4:
                    return R.layout.layout_four_motion_steps;
                default:
                    return -1;
            }
        }

        /**
         * 更新动作状态.
         */
        public void updateMotionStep(final int prevMotion, final int currentMotion) {
            for (MotionStep motionStep : mMotionStepBeans) {
                if (motionStep.motion == currentMotion) {
                    motionStep.updateStepState(MotionStep.StepState.STEP_CURRENT);
                }
                if (prevMotion == motionStep.motion) {
                    motionStep.updateStepState(MotionStep.StepState.STEP_COMPLETED);
                }
            }

        }

        /**
         * 添加要显示的动作.
         *
         * @param stepBeans stepBeans.
         */
        public void addMotionStepBeans(final List<MotionStep> stepBeans) {
            this.mMotionStepBeans.clear();

            if (stepBeans != null && !stepBeans.isEmpty()) {
                this.mMotionStepBeans.addAll(stepBeans);
            }
        }

        public void restart() {
            for (MotionStep motionStep : mMotionStepBeans) {
                motionStep.updateStepState(MotionStep.StepState.STEP_UNDO);
            }
        }
    }

    public static class MotionStep {

        private int motion;

        private String name;

        private StepState state;

        private TextView txtMotionName;

        private WaterRippleView waterRippleView;

        private View viewLine;

        public MotionStep(int motion, String name, StepState state) {
            this.motion = motion;
            this.name = name;
            this.state = state;
        }

        public void bindView(TextView txtMotionName, WaterRippleView waterRippleView, View viewLine) {
            this.txtMotionName = txtMotionName;
            this.txtMotionName.setText(name);
            this.waterRippleView = waterRippleView;
            this.viewLine = viewLine;
        }


        private void updateStepState(final StepState stepBeanState) {
            final float imageRatio = 28.0F / 30.0F;
            Context context = waterRippleView.getContext();
            switch (stepBeanState) {
                case STEP_COMPLETED:
                    if (viewLine != null) {
                        viewLine.setBackgroundColor(context.getResources().getColor(R.color.common_interaction_light_gray));
                    }
                    waterRippleView.stop();
                    waterRippleView.setCenterIconResource(R.drawable.common_ic_motion_step_done);
                    break;
                case STEP_CURRENT:
                    if (viewLine != null) {
                        viewLine.setBackgroundColor(context.getResources().getColor(R.color.common_interaction_ginger_yellow));
                    }
                    waterRippleView.setCenterImageRatio(imageRatio);
                    waterRippleView.setRippleSpeed(17);
                    waterRippleView.setRippleStrokeWidth(DensityUtil.dip2px(waterRippleView.getContext(), 8.0F));
                    waterRippleView.setCenterIconResource(R.drawable.common_ic_motion_step_ing);
                    waterRippleView.start();
                    break;
                case STEP_UNDO:
                    if (viewLine != null) {
                        viewLine.setBackgroundColor(context.getResources().getColor(R.color.common_interaction_light_gray));
                    }
                    waterRippleView.setCenterIconResource(R.drawable.common_ic_motion_step_wait);
                    waterRippleView.stop();
                    break;
                default:
                    break;
            }
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public StepState getState() {
            return this.state;
        }

        public void setState(StepState state) {
            this.state = state;
        }

        public enum StepState {
            STEP_UNDO,
            STEP_CURRENT,
            STEP_COMPLETED
        }
    }
}
